# Implementing Zero-Downtime CI/CD for Node.js Apps on AWS EC2

Deploying code sounds easy — until you’re doing it multiple times a day while keeping your production server stable.

Our Node.js backend powers critical features, so even a broken build or 2–3 minutes of downtime wasn’t acceptable. As our team grew, deployment became risky:

* Manual SSH deployments were slow
    
* Missed steps caused crashes
    
* Different machines had different Node versions
    
* Rollbacks were painful
    
* Hotfixes slowed down because the pipeline wasn’t automated
    

We needed a **bulletproof CI/CD pipeline**.

Below is the journey of how we moved from a patchy deployment process to a **fully automated CI/CD setup** using **GitHub Actions + AWS EC2 + PM2**.

---

# The Problem

## 1 — Manual Deployments Became a Bottleneck

Every update required logging into EC2, pulling code, installing dependencies, restarting PM2, and praying we didn’t break something.

During busy days, this became tedious — and errors slipped in.

## 2 — No Consistency Across Deployments

One laptop used Node 16, another had Node 18.  
Some deployments skipped tests.  
Some forgot `npm install`.  
Some overwrote `.env` accidentally.

We were relying on luck, not process.

## 3 — Zero Rollback Support

If something broke:

* We scrambled to revert to the previous commit
    
* Re-deployed manually
    
* Restarted PM2 again
    

Not fast, not reliable.

We needed automation.

---

# Step 1 — Setting Up GitHub Actions for CI

We began by automating the basics:

* Install Node
    
* Install dependencies
    
* Run tests
    
* Prepare build artifacts
    

GitHub Actions became our “CI engine”.

**Why GitHub Actions?**

* Close to the repository
    
* Easy secrets management
    
* Parallel jobs
    
* Free for most usage
    
* Very simple YAML-based workflows
    

This ensured every push was validated before deployment.

---

# Step 2 — Automating Deployment to AWS EC2

Next, we automated deployment.

Instead of SSH-ing manually to EC2, GitHub Actions now:

1. Builds the backend
    
2. Uploads the updated code to EC2
    
3. Restarts PM2 automatically
    
4. Verifies that the server comes back online
    

We securely stored:

* EC2 host
    
* SSH user
    
* Private key
    
* Environment variables
    

in **GitHub Secrets**, eliminating credential sharing.

This alone removed 90% of deployment friction.

---

# Step 3 — Introducing PM2 for Zero-Downtime Restarts

PM2 became our process manager.

It allowed:

* App restarts without downtime
    
* Crash auto-recovery
    
* CPU utilization monitoring
    
* Log management
    
* Easy rollbacks using saved snapshots
    

Whenever a deployment happened:

```plaintext
pm2 restart node-app
```

The app restarted instantly while keeping old connections alive.

Downtime: **0 seconds**.

---

# Step 4 — Handling Secure Environment Management

We moved all sensitive configuration from code into:

* `.env` file on EC2
    
* GitHub Secrets for CI
    
* PM2 ecosystem config for runtime variables
    

This ensured:

* No secrets in Git
    
* Easy environment change without redeploy
    
* Safer rollbacks
    

---

# Step 5 — Improving Deployment Reliability

We refined the pipeline further:

### Added health checks after deploy

To ensure the API booted correctly.

### Added rollback capability

By keeping previous build folders.

### Cleaned old build artifacts

To reduce EC2 disk usage over time.

### Added monitoring

Using PM2 logs + CloudWatch.

Small improvements → Big stability gains.

---

# Final Architecture Diagram

```plaintext
                 ┌─────────────────────────┐
                 │     GitHub Actions      │
                 │ (CI: Build & Test Code) │
                 └───────────┬─────────────┘
                             │
                             ▼
     ┌────────────────────────────────────────────────┐
     │        GitHub Actions Deployment Job           │
     │  1️⃣ SCP/SSH to EC2                             │
     │  2️⃣ Upload Build Files                         │
     │  3️⃣ Restart PM2                                │
     └───────────┬───────────────────────────┬────────┘
                 │                           │
     ┌───────────▼────────────┐   ┌──────────▼─────────────┐
     │      AWS EC2 Ubuntu    │   │   GitHub Secrets        │
     │  Node.js + PM2 Runtime │   │  (SSH, Host, Env Keys)  │
     └───────────┬────────────┘   └─────────────────────────┘
                 │
     ┌───────────▼────────────┐
     │   Node.js Application  │
     │ (Zero Downtime via PM2)│
     └────────────────────────┘
```

---

# Conclusion

Migrating to a CI/CD pipeline wasn’t just a tooling upgrade — it was an operational transformation.

With **GitHub Actions + EC2 + PM2**, we achieved:

* Fully automated deployments
    
* Zero downtime during restarts
    
* Safe and secure environment handling
    
* Consistent, reliable build process
    
* Faster development cycles
    
* No more late-night "quick fixes" breaking production
    

Today, pushing to `main` is all it takes for a clean, fast, safe deployment — every single time.
