Back to Blog
Lesson 15 of the CI/CD: Continuous Integration from Scratch course
DevOpsJuly 15, 20264 min read

Building Images in CI: Automating Docker Builds in GitHub Actions

Learn how to automate Docker builds in your CI pipeline. We’ll show you how to add a build step to GitHub Actions to ensure your application is always deployable.

DevOpsDockerCI/CDGitHub ActionsAutomation
Shipping containers and cranes at Hamburg port showcasing global trade.

Previously in this course, we explored Dockerizing the Pipeline, where we learned how to execute our test suite inside a containerized environment. In this lesson, we take the next logical step: moving from merely using a container to building one as a formal part of your CI pipeline.

Building images in CI ensures that your application is packaged correctly every time you push code. It acts as a "smoke test" for your Dockerfile—if the build fails, you know immediately that your application dependencies or environment configuration are broken before you even attempt a deployment.

Automating the Docker Build Process

When we talk about the Docker build process in CI, we aren't just running docker build . locally. We need to do this in a clean, ephemeral runner. GitHub Actions provides the environment, but it is our job to instruct the runner to invoke the Docker daemon and create the image.

The most reliable way to handle this is using the docker/build-push-action. This official action is optimized for GitHub Actions, offering features like layer caching and better integration with the GitHub environment.

A Concrete Worked Example

Assuming you have already followed the Introduction to Docker: Building Your First Container Image lesson, you likely have a valid Dockerfile in your root directory.

Here is how you add a build step to your existing YAML workflow:

YAML
name: CI Pipeline

on: [push]

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        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 # We only want to verify the build, not push it yet
          tags: my-app:latest

Why Use docker/build-push-action?

You might wonder why we don't just use run: docker build -t my-app .. While that works, the docker/build-push-action provides:

  1. BuildKit support: It automatically utilizes the latest Docker features for faster, more secure builds.
  2. Caching: It handles the complex logic of GitHub Actions cache restoration, making your builds significantly faster by reusing layers from previous runs.
  3. Multi-platform support: If you ever need to build for different architectures (like ARM64 for cloud instances), this action handles it natively.

Hands-on Exercise

Close-up of foam handle hand grippers for enhancing grip strength during workouts.

To verify your image creation, follow these steps:

  1. Update your workflow: Add the docker/setup-buildx-action and docker/build-push-action blocks to your current .github/workflows/main.yml file.
  2. Commit and Push: Push your changes to GitHub.
  3. Observe the logs: Navigate to the "Actions" tab in your repository. Click on the latest workflow run.
  4. Verify: Expand the "Build Docker image" step. You should see the Docker engine pulling layers, executing your RUN commands, and finishing with a success status.

Common Pitfalls

  • Ignoring the Context: If your Dockerfile expects files that aren't in the root, ensure the context parameter in your action is set to the correct directory.
  • Missing BuildKit: Trying to use advanced features without the setup-buildx-action will often result in confusing error messages. Always include the setup step.
  • Over-building: Don't build the image multiple times in the same job. If you need to test the image, build it once and use the resulting tag for subsequent steps.

Frequently Asked Questions

Close-up of a magnifying glass focusing on the phrase 'Frequently Asked Questions'.

Q: Do I need to push the image to a registry to verify it? A: No. By setting push: false, the image is built and stored in the local storage of the runner. If the build command exits with status code 0, the image is valid.

Q: Will this slow down my pipeline? A: Initial builds take time, but the docker/build-push-action handles caching. If your Dockerfile is optimized (e.g., placing COPY instructions after RUN npm install), subsequent builds will be very fast.

Q: Can I build multiple images in one pipeline? A: Yes, you can add multiple build steps or use a matrix strategy if you have a microservices architecture.

Recap

Wooden Scrabble tiles spelling 'RECAP' on a brown textured background. Text concept image.

In this lesson, we moved beyond testing code to validating the packaging process. By adding a Docker build step to our CI pipeline, we've created a safeguard that prevents broken images from ever reaching the registry. We’ve utilized the official docker/build-push-action to ensure our builds are fast, cached, and reproducible.

Up next: We will learn how to handle sensitive configuration without hardcoding it, as we dive into Managing Secrets.

Similar Posts