Orchestrating Container Delivery with Docker Build and Push Actions in GitHub Actions

The modernization of software delivery pipelines relies heavily on the ability to transform source code into portable, immutable artifacts. In the current DevOps landscape, GitHub Actions has emerged as a primary CI/CD platform, facilitating the automation of build, test, and deployment sequences. Central to this ecosystem is the integration of Docker, which allows developers to package applications and their dependencies into containers. The process of building and pushing these images to a registry is a critical juncture in the pipeline, ensuring that the exact version of the code tested in CI is the one deployed to production. Docker provides a comprehensive suite of official GitHub Actions designed to streamline this transition, offering a balance between ease of use through pre-defined components and the flexibility required for complex, enterprise-grade configurations.

By leveraging specialized actions, teams can move away from manual shell scripts and fragile custom implementations, instead utilizing standardized building blocks. These actions are engineered to interact with the Moby BuildKit builder toolkit, which is the modern engine for Docker builds. This integration enables advanced features such as multi-platform image creation, secure secret handling during the build phase, and sophisticated caching mechanisms that drastically reduce build times. Whether a project is being pushed to a public repository on Docker Hub or a private, highly secure registry like AWS ECR or Azure ACR, the architecture of these actions ensures a consistent and repeatable process.

The Ecosystem of Official Docker GitHub Actions

Docker does not provide a single monolithic tool but rather a modular set of official actions. This modularity allows engineers to compose a workflow that fits their specific infrastructure needs, whether they are using GitHub-hosted runners or self-hosted infrastructure.

The following table details the specialized actions available within the Docker ecosystem:

Action Name Primary Function Core Capability
Build and push Docker images Image Creation Utilizes BuildKit to build and push images
Docker Buildx Bake High-level Builds Enables complex build definitions via Bake
Docker Login Authentication Signs into various Docker registries
Docker Setup Buildx Builder Initialization Creates and boots a BuildKit builder
Docker Metadata Tagging & Labels Extracts Git references to generate tags
Docker Setup Compose Environment Setup Installs and configures Docker Compose
Docker Setup Docker Engine Installation Installs the Docker Engine on the runner
Docker Setup QEMU Multi-arch Support Installs QEMU static binaries for multi-platform builds
Docker Scout Security Analysis Analyzes images for security vulnerabilities

The inclusion of Docker Scout, for instance, transforms a simple build pipeline into a security-aware pipeline. By analyzing images for vulnerabilities before they are pushed to a registry, organizations can implement a "shift-left" security strategy, preventing compromised images from ever reaching a production environment. Similarly, the Docker Metadata action removes the manual burden of naming images, automatically deriving tags from GitHub events, which ensures a direct and traceable link between a specific Git commit and a specific container image.

Deep Technical Implementation of the Build and Push Process

The actual execution of building and pushing an image involves a coordinated sequence of steps. A failure in any single step—such as authentication or builder initialization—will halt the entire pipeline.

The foundational step in any Docker-based workflow is the checkout of the source code. Using actions/checkout@v4 is mandatory before any build action is invoked, as the builder requires access to the local filesystem to locate the Dockerfile and the build context.

Following the checkout, the environment must be prepared for BuildKit. The docker/setup-buildx-action@v3 is utilized to create and boot a builder. By default, this action employs the docker-container driver, which provides a more isolated and feature-rich environment than the standard Docker daemon. This is particularly critical for multi-platform builds, where the builder must be able to simulate different CPU architectures.

Authentication is handled via the docker/login-action@v3. This action abstracts the complexity of the docker login command, allowing the use of GitHub Secrets for sensitive credentials. The configuration typically requires the registry URL, the username, and the password.

Once the environment is authenticated and the builder is ready, the docker/build-push-action@v6 is invoked. This action serves as the primary engine for image creation.

Analyzing the build-push-action Configuration Parameters

The docker/build-push-action@v6 is highly configurable, allowing developers to control every aspect of the image creation process.

The following list describes the key parameters used during the build and push phase:

  • context: This defines the directory the builder uses as the build context. Using context: . tells the builder to use the root folder of the repository. In a monorepo architecture, this is vital; for example, a backend service and a frontend service can have separate jobs where the context is shifted to their respective directories.
  • file: This specifies the path to the Dockerfile. By default, the builder looks for a file named Dockerfile in the root, but by explicitly setting file: ./Dockerfile or a custom path like Dockerfile.ci, the user can control which build definition is used for specific environments.
  • push: A boolean value that determines if the resulting image should be uploaded to the registry. Setting push: true ensures that the image is not just built and tested locally but is made available for deployment.
  • tags: This is a multi-line input allowing the image to be pushed with multiple tags. It is common practice to push both a version-specific tag (e.g., 1.0.1) and the latest tag simultaneously to support both pinned versions and rolling updates.
  • cache-from: This specifies the source of the cache. Using type=registry,ref=... allows the builder to pull existing cache layers from the registry, which significantly accelerates the build process by avoiding the recompilation of unchanged layers.
  • cache-to: This defines where the new cache layers should be stored. Setting mode=max ensures that all layers, including intermediate ones, are cached, providing the highest possible speed for subsequent builds.

Advanced Workflow Integration and Semver Tagging

A sophisticated CI/CD pipeline does not trigger on every commit but rather on specific events. Using numeric semantic versioning (semver) tags allows a project to automate releases with confidence.

To achieve this, the workflow can be configured to trigger only on tags matching the pattern [0-9]+.[0-9]+.[0-9]+. This ensures that only tags like 1.0.0 or 20.15.10 trigger the build, preventing accidental releases from feature branches.

The process of extracting the version tag from the GitHub reference is handled via a shell command within the workflow. The following code snippet demonstrates how to isolate the version number from the Git ref:

bash echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT

This command strips the refs/tags/ prefix from the GITHUB_REF environment variable and stores the result in the GitHub Action output. This output is then referenced in the tags section of the build-push-action to ensure the Docker image is tagged identically to the Git release.

Comprehensive Example Workflow Configuration

The following configuration represents a complete, professional implementation of a build and push pipeline. It is designed to run on a self-hosted runner, although it can be adapted for ubuntu-latest GitHub-hosted runners.

```yaml

This workflow builds a Docker image and pushes it to a private registry

when a release tag (e.g., 1.0.1) is pushed to the repository.

env:
DOCKERIMAGENAME: my-image
DOCKER_REGISTRY: myregistry.mydomain.com

name: Build and Push Docker Image

on:
workflow_dispatch:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+'

jobs:
build-and-push-landing-page:
name: Build and Push Landing Page Docker Image
runs-on: self-hosted
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

  - name: Set up Docker Buildx
    uses: docker/setup-buildx-action@v3

  - name: Login to Docker Registry
    uses: docker/login-action@v3
    with:
      registry: ${{ env.DOCKER_REGISTRY }}
      username: ${{ secrets.DOCKER_USERNAME }}
      password: ${{ secrets.DOCKER_PASSWORD }}

  - name: Get the tag name
    id: get_version
    run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT

  - name: Build and push Landing Page Docker image
    uses: docker/build-push-action@v6
    with:
      context: .
      file: ./Dockerfile
      push: true
      tags: |
        ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.get_version.outputs.VERSION }}
        ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:latest
      cache-from: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:buildcache
      cache-to: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:buildcache,mode=max

```

In this implementation, the use of fetch-depth: 0 during checkout ensures that the full Git history is available, which is often necessary for certain build scripts or metadata actions. The cache-from and cache-to parameters use the registry type, which is the most efficient method for distributed CI environments where local disk cache is not persistent across different runner instances.

Registry Compatibility and Third-Party Alternatives

While the official Docker actions are the gold standard, the ecosystem supports a wide array of container registries. The build-push-action is compatible with:

  • Docker Hub
  • Google Container Registry (GCR)
  • Google Artifact Registry (GAR)
  • AWS Elastic Container Registry (ECR)
  • Azure Container Registry (ACR)
  • GitHub Docker Registry (GHCR)
  • JFrog Artifactory

For those utilizing alternative community actions, such as mr-smithers-excellent/docker-build-push@v6, the interface provides a slightly different set of inputs. These alternatives often include specialized boolean flags to simplify the tagging process.

The following table outlines the specific parameters available in the mr-smithers-excellent community action:

Parameter Requirement Type Description
image Yes String The name of the Docker image
tags No List Comma separated tags for the image
appendMode No Boolean Append tags to auto-generated GitOps tags
addLatest No Boolean Adds the latest tag to GitOps tags
addTimestamp No Boolean Suffixes build timestamp to branch-based tags
registry No String The URL of the registry

One critical change in recent updates to registry interactions, specifically for AWS ECR, is the deprecation of the get-login command. Modern workflows must migrate to the get-login-password command to maintain compatibility and security.

Final Technical Analysis of the Build Pipeline

The transition from manual Docker builds to an automated GitHub Actions pipeline represents a significant leap in operational maturity. The integration of BuildKit through setup-buildx-action is not merely a convenience but a requirement for modern containerization. Multi-platform support, for instance, allows a single workflow to produce images for amd64, arm64, and other architectures, ensuring that the application can run across diverse cloud and edge environments.

The strategic use of caching via the registry type transforms the build process from a linear time-cost to a logarithmic one. By storing cache layers in the registry, the pipeline avoids redundant work, which is especially impactful for large images containing heavy dependency installations (e.g., Node.js npm install or Python pip install steps).

Furthermore, the decoupling of the build context and the Dockerfile allows for sophisticated repository structures. In a monorepo, the ability to specify a specific context ensures that only the necessary files are sent to the Docker daemon, reducing the "build context" upload time and preventing the inclusion of unnecessary files in the image layers, which would otherwise bloat the final image size.

The shift towards using semantic versioning triggers ensures that the deployment pipeline is predictable. By constraining the push event to numeric semver tags, organizations can guarantee that every image in the registry corresponds to a specific, immutable release version, effectively eliminating the "it works on my machine" syndrome by ensuring that the environment in the registry is an exact replica of the versioned source code.

Sources

  1. Docker Build GitHub Actions
  2. docker/build-push-action GitHub Repository
  3. GitHub Marketplace - Build and Push Docker Images
  4. OneUptime - GitHub Actions Docker Build and Push Guide
  5. Daniel Es Blog - Automatically Build Docker Images with GitHub Actions
  6. GitHub Marketplace - docker-build-push-action

Related Posts