DEV Community

Omkar Sharma
Omkar Sharma

Posted on

Deploy Node.js & Express Application Using CI/CD (GitHub Actions + Docker)

Omkar Sharma

Quick Flow before deep dive

  1. Developer pushes code to GitHub
  2. GitHub Actions triggers pipeline
  3. Pipeline connects to EC2 via SSH
  4. Pulls latest code
  5. Rebuilds Docker image
  6. Runs container using Docker Compose
  7. Application is live on EC2 public IP

Prerequisites

  • Node.js (v16+) and npm installed on local machine

  • Git installed on local machine and basic commands knowledge

  • Docker installed on local machine

  • GitHub account

  • AWS account

  • Basic cloud/devops knowledge

1. Create a Simple Node.js + Express Application

Initialize a Node.js project:

npm init
Enter fullscreen mode Exit fullscreen mode

Install required dependencies:

npm install express
npm install @types/express --save-dev
Enter fullscreen mode Exit fullscreen mode

2. Update package.json

Add the following:

{
  "name": "node-app",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start": "node index.js"
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Create index.js

import express from 'express';

const app = express();
const PORT = process.env.PORT || 8080;

app.get('/', (req, res) => {
  res.send('Hello from the server!');
});

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

4. Create a Dockerfile

FROM node:22-alpine

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .

EXPOSE 8080

CMD [ "node" , "index" ]
Enter fullscreen mode Exit fullscreen mode

5. Build Docker Image

docker build -t node-app .
Enter fullscreen mode Exit fullscreen mode

Explanation

  • docker build → Builds a Docker image
  • -t node-app → Tags the image with the name node-app
  • . → Refers to the current directory (build context)

Purpose of . (dot):
It tells Docker to use the current folder as the context, meaning all files in this directory are available to the Docker daemon during build.

6. Run the Container

Note: Before running below command make sure docker engine is running state........ for testing trying to run container before creating compose file after creating compose file we dont need to run container manually just compose up commmand will take care of everything

docker run -d -p 8080:8080 --rm node-app
Enter fullscreen mode Exit fullscreen mode

Explanation

  • -d → Runs container in detached mode
  • -p 8080:8080 → Maps host port 8080 to container port 8080
  • --rm → Automatically removes the container when it stops

Purpose of --rm:

  • Prevents accumulation of stopped containers
  • Keeps the system clean
  • Useful for temporary/testing containers

7. Test Using curl

curl http://localhost:8080
Enter fullscreen mode Exit fullscreen mode

Omkar Sharma

curl is a command-line tool used to transfer data to and from a server using URLs.

Expected output:

Hello from the server!
Enter fullscreen mode Exit fullscreen mode

8. Create Docker Compose File

Create docker-compose.yml:

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    restart: unless-stopped
    ports:
      - "8080:8080"
Enter fullscreen mode Exit fullscreen mode

Run the Application

docker compose up -d
Enter fullscreen mode Exit fullscreen mode

In browser test the application

omkar sharma

Stop the Application

docker compose down
Enter fullscreen mode Exit fullscreen mode

Verify Again

curl http://localhost:8080
Enter fullscreen mode Exit fullscreen mode

9. Push Code to GitHub

Check my github repo for this task - omkarsharma2821

Initialize Git and push code:

git init
git add .
git commit -m "Initial commit"

git branch -M main

git remote add origin <your-repo-url>

git push -u origin main
Enter fullscreen mode Exit fullscreen mode

10. Create an EC2 Instance (Ubuntu)

Once the instance is created, connect via SSH and run:

sudo apt update
sudo apt upgrade -y
Enter fullscreen mode Exit fullscreen mode

Importance of Update & Upgrade

  • Updates package list from repositories
  • Installs latest security patches
  • Fixes vulnerabilities
  • Ensures system stability before installing new software

11. Install Docker & Docker Compose

Option 1: Install from Ubuntu Repository

sudo apt install docker.io -y
sudo apt install docker-compose -y
Enter fullscreen mode Exit fullscreen mode

Option 2: Install from Official Docker Website

Recommended for:

  • Latest stable version
  • Better performance and features
  • Production environments

12. Run Application on EC2

Clone your repository:

git clone <your-repo-url>
cd <repo-name>
Enter fullscreen mode Exit fullscreen mode

Run:

docker compose up -d
Enter fullscreen mode Exit fullscreen mode

Test Application

curl http://<EC2-PUBLIC-IP>:8080
Enter fullscreen mode Exit fullscreen mode

make sure you have allowed port no. 8080 in the instance security group to get the response.

13. Setup GitHub Actions (CI/CD)

Create workflow file:

.github/workflows/deploy.yml
Enter fullscreen mode Exit fullscreen mode

Example:

name: Deploy Node App

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

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

      - name: Deploy via SSH
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            set -e

            cd /home/ubuntu/node-app/Node.js-App-Deploy-Github-Action

            echo "Pulling latest code..."
            git pull origin main

            echo "Stopping existing containers..."
            docker compose down

            echo "Removing unused images..."
            docker image prune -f

            echo "Building and starting containers..."
            docker compose up -d --build

            echo "Deployment completed successfully"
Enter fullscreen mode Exit fullscreen mode

**set -e**
Stops execution if any command fails
Prevents partial deployments
**Cleanup Step**
docker image prune -f
Prevents disk space issues over time

14. Generate SSH Keys

On local machine:

ssh-keygen -t rsa -b 4096
Enter fullscreen mode Exit fullscreen mode

Setup

  • Copy public key to EC2:
cat ~/.ssh/id_rsa.pub
Enter fullscreen mode Exit fullscreen mode

Paste it into:

~/.ssh/authorized_keys
Enter fullscreen mode Exit fullscreen mode
  • Add private key to GitHub:

Go to:

GitHub → Repo → Settings → Secrets → Actions
Enter fullscreen mode Exit fullscreen mode

Omkar Sharma

Add:

SSH_KEY - <your-key>
SSH_HOST - <public-ip of ec2>
SSH_USERNAME - <root or ubuntu>
Enter fullscreen mode Exit fullscreen mode

✍️ Author: Omkar Sharma

📬 Feel free to connect on LinkedIn or explore more on GitHub

Top comments (0)