BACK TO INSIGHTS
TECH: DevOps & CloudJAN 30, 20266 MIN READBY Anuj Bansal

CI/CD Pipelines That Just Work Every Single Time

How we build reliable, secure and fast CI/CD pipelines using GitHub Actions, Docker and smart caching.

SHARE THIS ARTICLE:
DevOps & Cloud
GIT PUSHACTIONSLint PassDOCKER<150MBBuild [✓]AWS EC2LIVE 99.9%
💡Key Takeaways & Executive Summary
  • Multi-stage Docker builds eliminate development dependencies from final production images, slashing image sizes from 1.2GB to <150MB.
  • Cache node_modules and Docker layers in GitHub Actions to reduce CI workflow execution times from 8 minutes to under 90 seconds.
  • Deploy updates using PM2 cluster reload to achieve zero-downtime rolling releases on production servers.
  • Implement automated health check steps that roll back deployments immediately if an endpoint returns a non-200 status code.

Deployment pipelines should operate predictably without manual intervention. Poorly structured configurations frequently fail due to unbudgeted memory limits during Next.js builds, slow un-cached Docker layer downloads, or environment inconsistencies between staging and production.

A reliable deployment pipeline combines multi-stage container optimization with automated GitHub Actions execution.

1. Multi-Stage Dockerfile for Next.js & Node.js

Separating dependency resolution, build compilation, and production runtime keeps final container images under 150MB:

dockerfile
# Stage 1: Dependency Installation
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production

# Stage 2: Application Build FROM node:20-alpine AS builder WORKDIR /app COPY --from=deps /app/nodemodules ./nodemodules COPY . . RUN npm run build

# Stage 3: Minimal Production Runner (<150MB) FROM node:20-alpine AS runner WORKDIR /app ENV NODE_ENV=production COPY --from=builder /app/public ./public COPY --from=builder /app/.next/standalone ./ COPY --from=builder /app/.next/static ./.next/static

EXPOSE 3000 CMD ["node", "server.js"] ```

2. Deterministic GitHub Actions Deployment Workflow

yaml
name: Production Deployment Pipeline

on: push: branches: [ main ]

jobs: validate-and-deploy: runs-on: ubuntu-latest steps: - name: Checkout Source Code uses: actions/checkout@v4

- name: Setup Node.js Environment uses: actions/setup-node@v4 with: node-version: 20 cache: 'npm'

- name: Install Dependencies run: npm ci

- name: Run Type Check & Linter run: | npx tsc --noEmit npm run lint

- name: Deploy to Remote Server via SSH uses: appleboy/ssh-action@master with: host: ${{ secrets.EC2HOST }} username: ${{ secrets.EC2USER }} key: ${{ secrets.SSHPRIVATEKEY }} script: | cd /var/www/anuj-portfolio git pull origin main npm ci --only=production npm run build pm2 reload ecosystem.config.js --update-env curl -f http://localhost:3000/api/health || exit 1 ```

Using npm ci enforces exact package-lock.json checksums across environments, while PM2 cluster reloading seamlessly shifts active connections to updated worker threads without dropping incoming client requests.

AB
ABOUT THE AUTHOR

Anuj Bansal

Anuj Bansal is a freelance full stack developer based in Indore, India specializing in scalable Next.js architectures, React web applications, Node.js backends, and high-performance server infrastructure. Looking to build a production-ready product? Hire Anuj for your next web application or SaaS platform.

🚀READY FOR PRODUCTION

Planning to Build a Production-Ready Web Application?

From system architecture and Next.js engineering to database optimization and technical SEO — let’s build a fast, scalable web product tailored to your business goals.

Available for New Projects Free 30-Min Architecture Discovery💬 Direct Engineering Access
Current: 1. Multi-Stage Dockerfile for Next.js & Node.js

Get engineering notes in your inbox.

Real-world lessons, system design deep-dives, and production stories — delivered weekly.