github-actions

GitHub Actions Build and Push Docker Image

GitHub Actions workflow to build, tag, and push Docker images to container registries with multi-platform and caching support.

Overview

A GitHub Actions workflow that builds Docker images and pushes them to GitHub Container Registry (GHCR). Supports multi-platform builds, layer caching, semantic version tagging, and security scanning.

Configuration

# .github/workflows/docker.yml

name: Build and Push Docker Image

on:
  push:
    branches: [main]
    tags: ["v*"]                       # Trigger on version tags
  pull_request:
    branches: [main]                   # Build (no push) on PRs

concurrency:
  group: docker-${{ github.ref }}
  cancel-in-progress: true

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}  # e.g., org/repo

jobs:
  build:
    name: Build & Push
    runs-on: ubuntu-latest

    permissions:
      contents: read
      packages: write                  # Push to GHCR
      attestations: write              # Sign images
      id-token: write                  # OIDC for signing

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

      # Set up QEMU for multi-platform builds
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      # Set up Docker Buildx for advanced build features
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      # Log in to container registry
      - name: Log in to GHCR
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      # Generate image tags and labels from Git metadata
      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            # Tag with branch name
            type=ref,event=branch
            # Tag with PR number
            type=ref,event=pr
            # Tag with semver from Git tag (v1.2.3 -> 1.2.3, 1.2, 1)
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=semver,pattern={{major}}
            # Tag with Git SHA (short)
            type=sha,prefix=
            # Tag latest for default branch
            type=raw,value=latest,enable={{is_default_branch}}

      # Build and push the image
      - name: Build and push
        id: build
        uses: docker/build-push-action@v5
        with:
          context: .
          platforms: linux/amd64,linux/arm64   # Multi-platform
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha                 # GitHub Actions cache
          cache-to: type=gha,mode=max

      # Scan for vulnerabilities
      - name: Run Trivy vulnerability scanner
        if: github.event_name != 'pull_request'
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
          format: sarif
          output: trivy-results.sarif
          severity: CRITICAL,HIGH

      - name: Upload Trivy scan results
        if: github.event_name != 'pull_request'
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: trivy-results.sarif

Key Options Explained

  • docker/metadata-action — Automatically generates proper OCI image tags from Git refs. A v1.2.3 tag produces 1.2.3, 1.2, 1, and latest image tags.
  • docker/build-push-action — Uses Buildx for advanced features like multi-platform builds, build caching, and inline cache metadata.
  • platforms: linux/amd64,linux/arm64 — Builds for both x86 and ARM architectures in a single workflow, producing a multi-arch manifest.
  • cache-from/cache-to: type=gha — Uses GitHub Actions cache backend to store and restore Docker layer cache, dramatically speeding up rebuilds.
  • push: ${{ github.event_name != 'pull_request' }} — Only pushes images for non-PR events. PRs still build the image (validating the Dockerfile) but don’t publish.
  • Trivy scanner — Scans the built image for known CVEs. Results appear in GitHub’s Security tab as SARIF alerts.

Common Modifications

  • AWS ECR: Replace GHCR login with aws-actions/amazon-ecr-login@v2 and set REGISTRY to your ECR URL.
  • Docker Hub: Use docker/login-action with username: ${{ secrets.DOCKERHUB_USERNAME }} and password: ${{ secrets.DOCKERHUB_TOKEN }}.
  • Build args: Add build-args: | with NODE_ENV=production or VERSION=${{ github.ref_name }} to the build step.
  • Single platform: Remove the QEMU step and platforms line to build only for linux/amd64 (faster builds).
  • Deploy after push: Add a deploy job with needs: build that updates your Kubernetes deployment or ECS service with the new image tag.