DevOps isn't complete without automation. Once you've containerized your app, it's time to automate everything from building and testing to deploying it across environments. This post dives into integrating Docker with CI/CD pipelines using tools like GitHub Actions, writing multi-stage Dockerfiles, and optimizing builds with caching strategies.
Here's how Docker fits into a CI/CD pipeline:
DockerfileMulti-stage builds help you build and package your app efficiently by separating build dependencies from the final image. Here's a production-ready example for a Node.js app:
# Stage 1 โ Build
FROM node:18-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2 โ Production
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production
CMD ["node", "dist/index.js"]
Why it matters: Final image is smaller, faster, and contains no unnecessary dev files.
Let's walk through a full CI/CD setup using GitHub Actions.
.github/workflows/docker-ci.ymlname: CI/CD with Docker
on:
push:
branches: [main]
jobs:
build-test-deploy:
runs-on: ubuntu-latest
steps:
- name: ๐ฅ Checkout code
uses: actions/checkout@v3
- name: ๐ณ Set up Docker
uses: docker/setup-buildx-action@v2
- name: ๐จ Build image
run: docker build -t aelify/app:latest .
- name: โ
Run tests in container
run: docker run --rm aelify/app:latest npm test
- name: ๐ Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: ๐ Push to Docker Hub
run: docker push aelify/app:latest
- name: ๐ข Deploy (optional)
run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SERVER_IP }} \
'docker pull aelify/app:latest && docker restart my-container'
๐ Secrets like Docker credentials or SSH keys should be stored securely in GitHub Secrets.
Want to test your app exactly how it'll run in production? Docker is perfect:
docker build -t test-app .
docker run --rm test-app npm test
๐ฅ Tip: Include test dependencies only in the first stage of your Dockerfile.
Here's a smart way to cache dependencies in Docker:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
By copying package.json separately, Docker caches dependencies unless they change โ drastically
speeding up builds.
If you use GitLab, here's a similar pipeline:
build:
image: docker:latest
services:
- docker:dind
script:
- docker build -t registry.gitlab.com/youruser/app:latest .
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
- docker push registry.gitlab.com/youruser/app:latest
Docker + CI/CD = shipping confidently and consistently. Whether you're deploying weekly or hourly, these tools let you move fast and stay reliable. Start small, automate relentlessly, and enjoy the ride. ๐งช๐ข๐ก
In our next post, we'll dive into ๐ (Post 7): Securing Docker โ Best Practices for Hardening Containers & Hosts!
โ Blog by Aelify (ML2AI.com)
๐ Documentation Index