Docker Full Tutorial — Part 10: Docker in CI/CD Pipelines

By Suraj Ahir December 07, 2025 6 min read

Docker — Docker Security
Docker — Docker Security
← Part 9 Docker Tutorial · Part 10 of 12 Part 11 →

Building Docker images manually on your laptop works fine for personal projects. But in a professional team, every code change should automatically trigger a build, test run, and deployment. This is CI/CD — Continuous Integration and Continuous Deployment. Docker fits perfectly into CI/CD pipelines because containers make builds reproducible and deployments consistent.

What CI/CD Means in the Docker World

The Docker CI/CD workflow looks like this: a developer pushes code to Git, the CI system (GitHub Actions, GitLab CI, Jenkins) automatically builds a Docker image, runs tests inside that image, pushes the image to a registry with a version tag, and optionally deploys it to staging or production. Every step is automated, reproducible, and auditable.

GitHub Actions — Docker Build and Push

GitHub Actions is one of the most popular CI/CD platforms. Here is a complete workflow that builds your Docker image and pushes it to Docker Hub on every push to the main branch:

.github/workflows/docker-build.yml
name: Build and Push Docker Image

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

env:
  DOCKER_IMAGE: yourusername/my-app

jobs:
  build-and-push:
    runs-on: ubuntu-latest

    steps:
      # Checkout code
      - name: Checkout repository
        uses: actions/checkout@v4

      # Set up Docker Buildx (needed for multi-platform builds)
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      # Login to Docker Hub
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      # Extract metadata for tagging
      - name: Extract Docker metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.DOCKER_IMAGE }}
          tags: |
            type=ref,event=branch
            type=sha,prefix=
            type=semver,pattern={{version}}

      # Build and push
      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Setting Up GitHub Secrets

Never put credentials directly in workflow files. Use GitHub repository secrets instead. Go to your repository Settings → Secrets and variables → Actions → New repository secret. Add DOCKERHUB_USERNAME and DOCKERHUB_TOKEN (use an access token, not your password).

Running Tests in Docker During CI

Test Stage in CI
      # Run tests using Docker
      - name: Run tests
        run: |
          docker build --target test -t my-app:test .
          docker run --rm my-app:test pytest tests/ -v

      # Or using docker compose
      - name: Run integration tests
        run: |
          docker compose -f docker-compose.test.yml up --abort-on-container-exit
          docker compose -f docker-compose.test.yml down

Build Caching for Faster Pipelines

Docker builds without caching are slow. GitHub Actions provides a cache mechanism that stores Docker layers between runs. The cache-from and cache-to options in the build-push-action handle this automatically. For a typical Python project, caching reduces build time from 5+ minutes to under 1 minute after the first run.

Multi-Platform Builds

With Apple Silicon Macs common in development teams, building images for both AMD64 (x86_64) and ARM64 is often required:

Multi-Platform Build
      - name: Build multi-platform image
        uses: docker/build-push-action@v5
        with:
          platforms: linux/amd64,linux/arm64
          push: true
          tags: yourusername/my-app:latest

Deployment After Build

Deploy to Server via SSH
      - name: Deploy to production server
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            docker pull yourusername/my-app:${{ github.sha }}
            docker stop my-app || true
            docker rm my-app || true
            docker run -d               --name my-app               -p 5000:5000               --restart unless-stopped               yourusername/my-app:${{ github.sha }}

In Part 11, we will explore Docker Swarm — Docker's built-in container orchestration for running containers across multiple servers with load balancing and automatic failover.

GitHub Actions Docker Integration

GitHub Actions is one of the most widely used CI/CD platforms and has excellent Docker support. A typical workflow builds, tests, and pushes a Docker image on every push to main:

GitHub Actions Docker Workflow
name: Build and Push Docker Image

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_TOKEN }}
      
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            myuser/myapp:latest
            myuser/myapp:${{ github.sha }}

Store Docker Hub credentials as GitHub repository secrets, not in the workflow file. The github.sha context variable provides the commit hash for traceability — you can always identify exactly which code version a running container contains.

Testing Containers in CI

Before pushing an image, run tests against it in CI. Build the image, start it as a service in the CI job, wait for it to be healthy, then run your test suite against it. This validates not just that the code works but that it works in the containerized environment — catching issues like missing environment variables, missing files, or incorrect permissions that only manifest in the container context.

Practice Exercise

Set up a GitHub Actions workflow for a simple application: the workflow should trigger on push, build the Docker image, run a container from the built image and verify it starts correctly (using a health check or a curl request), and push the image to Docker Hub if tests pass. Store your Docker Hub credentials as GitHub secrets. Verify the workflow runs successfully by pushing a change and watching the Actions tab.

Building Cloud Intuition Over Time

Cloud computing is a domain where deep intuition — the ability to make good architectural decisions quickly, to diagnose problems efficiently, and to anticipate how systems will behave under load — develops through accumulated hands-on experience. Every project you build on cloud infrastructure teaches you something that cannot be learned from documentation alone. The cost surprises, the permission errors, the networking debugging sessions, the performance investigations — these are not obstacles to learning, they are the learning. The engineers who have built genuinely deep cloud intuition have usually accumulated it through many projects over several years, not from any single course or certification. Start building things, make mistakes safely in learning environments, and accumulate that experience deliberately.

Disclaimer: This content is for educational purposes only. SRJahir Tech does not guarantee any specific outcome or job placement.