DevOps
February 8, 202610 min readSetting Up CI/CD with Docker and GitHub Actions: A 2026 Guide
Learn how to build a complete CI/CD pipeline using Docker and GitHub Actions. From multi-stage builds to automated deployments, this guide covers everything you need for production-ready workflows.
By TechLead
Docker
CI/CD
GitHub Actions
DevOps
Deployment
A well-designed CI/CD pipeline is the backbone of modern software delivery. By combining Docker for consistent environments with GitHub Actions for automation, you can build, test, and deploy with confidence. This guide walks you through setting up a production-grade pipeline from scratch.
1. Why Docker + GitHub Actions?
- Reproducibility: Docker ensures your app builds and runs the same way everywhere — your laptop, CI server, and production.
- Speed: Multi-stage builds and layer caching dramatically reduce build times.
- Free tier: GitHub Actions offers 2,000 minutes/month for free on public repos.
2. Writing a Production Dockerfile
Use multi-stage builds to keep your final image lean:
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Stage 2: Production
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["npm", "start"]
This pattern typically reduces image size by 60-80% compared to a single-stage build.
3. GitHub Actions Workflow
Create .github/workflows/deploy.yml:
name: Build and Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: .
push: false
load: true
tags: myapp:test
- name: Run tests in container
run: docker run --rm myapp:test npm test
deploy:
needs: build-and-test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:latest
4. Key Optimizations
- Layer caching: Docker builds only changed layers. Order your Dockerfile from least-to-most frequently changed (OS → dependencies → source code).
- BuildKit:
docker/setup-buildx-actionenables BuildKit by default, giving you parallel builds and better caching. - .dockerignore: Exclude
node_modules,.git, and test files to speed up the build context.
5. Adding Health Checks
Add a health check to your Dockerfile so orchestrators know when your app is ready:
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 CMD curl -f http://localhost:3000/api/health || exit 1
6. Security Best Practices
- Never store secrets in Docker images. Use GitHub Actions secrets and inject them at runtime.
- Run containers as a non-root user:
USER node - Scan images for vulnerabilities:
docker scout cves myapp:latest - Pin base image versions instead of using
:latest