Setting Up CI/CD for a Node.js CRUD API with GitHub Actions, Docker, and AWS EC2

Setting up CI/CD pipelines for deploying applications is crucial for delivering software efficiently and reliably. In this guide, we'll walk through how to create a CI/CD pipeline for a Node.js application with CRUD APIs. We'll use GitHub Actions, Docker, and an AWS EC2 instance with GitHub runners.


Prerequisites

Before we begin, ensure you have the following ready:

  1. Node.js CRUD API: A simple Node.js application with basic CRUD APIs.

  2. GitHub Repository: Your application is pushed to a GitHub repository.

  3. Docker Hub Account: To store and manage your Docker images.

  4. AWS EC2 Instance: Set up an Ubuntu-based EC2 instance.

  5. MongoDB Instance: MongoDB should be accessible either locally or via a cloud service like MongoDB Atlas.

  6. GitHub Secrets: Store sensitive credentials securely in GitHub Secrets:

    • DOCKER_USERNAME and DOCKER_PASSWORD: Your Docker Hub credentials.

    • MONGO_PASSWORD: MongoDB password.


Overview of Our Workflow

  1. Build and Push Docker Image:

    • Package the Node.js app into a Docker image.

    • Push the image to Docker Hub.

  2. Deploy on AWS EC2:

    • Pull the Docker image from Docker Hub.

    • Run the container on EC2 using GitHub self-hosted runners.


Step 1 : Create a Dockerfile

In your project root, create a Dockerfile to containerize your application:

# Use Node.js alpine3.18 image
FROM node:alpine3.18

# Set the working directory
WORKDIR /app

# Copy package.json and install dependencies
COPY package*.json ./
RUN npm install

# Copy the source code
COPY . .

# Expose the application port
EXPOSE 9000

# Start the application
CMD ["npm", "start"]

Step 2: Create the GitHub Actions Workflow

Add a GitHub Actions workflow file to automate the CI/CD process. Save it as .github/workflows/cicd.yml:

name: Deploy Node Application

on:
  push:
    branches:
      - mern-ec2-docker  # Trigger workflow on commits to this branch

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Source
        uses: actions/checkout@v4

      - name: Login to Docker Hub
        run: docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}

      - name: Build Docker Image
        run: docker build -t patilprathamesh/nodejs-app .

      - name: Publish Image to Docker Hub
        run: docker push patilprathamesh/nodejs-app:latest

  deploy:
    needs: build
    runs-on: self-hosted  # Self-hosted runner on EC2
    steps:
      - name: Pull Image from Docker Hub
        run: docker pull patilprathamesh/nodejs-app:latest

      - name: Delete Old Container
        run: docker rm -f nodejs-app-container || true

      - name: Run Docker Container
        run: docker run -d -p 9000:9000 --name nodejs-app-container -e MONGO_PASSWORD=${{ secrets.MONGO_PASSWORD }} patilprathamesh/nodejs-app

Step 3: Configure the AWS EC2 Instance

  1. Install Docker: SSH into your (Linux Ubuntu) EC2 instance and install Docker.

     sudo apt-get update
     sudo apt-get install docker.io -y
     sudo systemctl start docker
     sudo docker run hello-world
     sudo chmod 666 /var/run/docker.sock
     docker ps
     sudo systemctl enable docker
     docker --version
    
  2. Install GitHub Runner: Follow the GitHub self-hosted runner setup guide to configure the runner on your EC2 instance.

  3. Set Up MongoDB: Ensure MongoDB is available to your application. You can:

    • Use a locally installed MongoDB.

    • Use MongoDB Atlas or another cloud database provider.


Step 4: Debugging Common Issues

Here are some common issues you might encounter:

  1. Port Mapping Issues:

    • Ensure the port exposed in the Dockerfile matches the one mapped on the EC2 instance (-p 9000:9000).
  2. Dockerfile Errors:

    • Make sure your Dockerfile is correctly configured to install dependencies and expose the required ports.
  3. Environment Variables in Docker:

    • Use -e flags in the docker run command to pass secrets (e.g., MONGO_PASSWORD).
  4. GitHub Secrets:

    • Verify that secrets are correctly added in the GitHub repository settings and referenced in the cicd.yml file.
  5. Self-Hosted Runner Connectivity:

    • Ensure your self-hosted runner is online and properly linked to GitHub Actions.

Step 5: Verify Deployment

After pushing changes to your GitHub repository:

  1. The CI/CD pipeline should build and push the Docker image to Docker Hub.

  2. The deployment job should pull the image and run it on the EC2 instance.

You can verify the deployment by accessing your application at http://<EC2_PUBLIC_IP>:9000.


Conclusion

Setting up CI/CD for a Node.js application using GitHub Actions, Docker, and AWS EC2 is a powerful way to streamline your development process. While you might encounter challenges initially, they offer valuable learning opportunities and help you build a robust, automated deployment pipeline.

Now, your application is set up to automatically build, test, and deploy whenever you push changes to the mern-ec2-docker branch! 🎉

Let me know if you need help troubleshooting specific issues!


Stay Tuned for Updates!

In the coming days, I’ll be:

  • Designing the UI/UX for the React app.

  • Writing the cicd.yml file for the frontend.

  • Deploying the full-stack app and making it accessible to the world.

I’ll share my learnings, challenges, and solutions along the way. If you’re on a similar journey or curious about modern CI/CD workflows, join me as I build the complete Node.js + React.js full-stack app with fully automated deployments!

Next stop: A fully deployed full-stack project! 🚀

0
Subscribe to my newsletter

Read articles from Prathamesh Patil directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Prathamesh Patil
Prathamesh Patil