GitHub Actions: Docker Build and Push

Type: Software Reference Confidence: 0.93 Sources: 7 Verified: 2026-02-28 Freshness: 2026-02-28

TL;DR

Constraints

Quick Reference

ActionVersionPurposeKey Inputs
docker/setup-qemu-action@v3Enable multi-platform via QEMUplatforms
docker/setup-buildx-action@v3Create Buildx builderdriver-opts
docker/login-action@v3Authenticate to registryregistry, username, password
docker/metadata-action@v5Generate tags/labels from Gitimages, tags
docker/build-push-action@v6Build and push imagecontext, push, tags, platforms, cache
GHCR loginGitHub Container Registryregistry: ghcr.io, GITHUB_TOKEN
Docker Hub loginDocker HubDOCKERHUB_TOKEN secret
ECR login@v2AWS ECRAWS credentials
Cache (GHA)GitHub Actions cache backendtype=gha, mode=max
Cache (registry)Registry as cachetype=registry, ref=...

Decision Tree

START
├── Single platform (amd64 only)?
│   ├── YES → setup-buildx + login + metadata + build-push (no QEMU)
│   └── NO ↓
├── Multi-platform (amd64 + arm64)?
│   ├── YES → Add setup-qemu + platforms: linux/amd64,linux/arm64
│   └── NO ↓
├── Push to GHCR?
│   ├── YES → login with GITHUB_TOKEN, registry: ghcr.io
│   └── NO ↓
├── Push to Docker Hub?
│   ├── YES → login with DOCKERHUB_TOKEN
│   └── NO ↓
├── Push to AWS ECR?
│   ├── YES → aws-actions/configure-aws-credentials + amazon-ecr-login
│   └── NO ↓
└── DEFAULT → Build without push (CI validation): push: false

Step-by-Step Guide

1. Create the workflow file

Create .github/workflows/docker.yml for building and pushing Docker images. [src1]

name: Docker Build and Push

on:
  push:
    branches: [main]
    tags: ['v*']
  pull_request:
    branches: [main]

permissions:
  contents: read
  packages: write

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - id: meta
        uses: docker/metadata-action@v5
        with:
          images: ghcr.io/${{ github.repository }}
      - uses: docker/build-push-action@v6
        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

Verify: Push to main → image appears at ghcr.io/<owner>/<repo>:main.

2. Configure semantic versioning tags

Use metadata-action to auto-tag from Git. [src5]

      - id: meta
        uses: docker/metadata-action@v5
        with:
          images: ghcr.io/${{ github.repository }}
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha,prefix=

Verify: Push tag v1.2.3 → image gets tags: 1.2.3, 1.2, and SHA.

3. Add multi-platform support

Enable QEMU emulation for multi-arch builds. [src4]

      - uses: docker/setup-qemu-action@v3
      - uses: docker/setup-buildx-action@v3
      - uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ${{ steps.meta.outputs.tags }}

Verify: docker manifest inspect shows both amd64 and arm64.

4. Enable GitHub Actions cache

Use GHA cache backend for faster builds. [src1]

      - uses: docker/build-push-action@v6
        with:
          cache-from: type=gha
          cache-to: type=gha,mode=max

Verify: Second build shows "importing cache manifest" and completes faster.

Code Examples

Complete GHCR workflow with caching and semver

# .github/workflows/docker.yml
name: Docker Build & Push

on:
  push:
    branches: [main]
    tags: ['v*.*.*']
  pull_request:
    branches: [main]

permissions:
  contents: read
  packages: write

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - if: github.event_name != 'pull_request'
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha,prefix=
      - uses: docker/build-push-action@v6
        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

Multi-platform build with QEMU

# .github/workflows/docker-multiplatform.yml
name: Multi-Platform Docker Build

on:
  push:
    tags: ['v*.*.*']

permissions:
  contents: read
  packages: write

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-qemu-action@v3
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - id: meta
        uses: docker/metadata-action@v5
        with:
          images: ghcr.io/${{ github.repository }}
      - uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Anti-Patterns

Wrong: Building without Buildx

# ❌ BAD — docker build without Buildx cannot use layer caching
- run: docker build -t myimage:latest .
- run: docker push myimage:latest

Correct: Using Docker official actions with Buildx

# ✅ GOOD — Buildx enables caching, multi-platform, advanced features
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
  with:
    context: .
    push: true
    tags: myimage:latest
    cache-from: type=gha
    cache-to: type=gha,mode=max

Wrong: Hardcoding image tags

# ❌ BAD — hardcoded tags don't track versions
- uses: docker/build-push-action@v6
  with:
    tags: ghcr.io/myorg/myapp:latest

Correct: Using metadata-action for dynamic tags

# ✅ GOOD — generates tags from Git refs automatically
- uses: docker/metadata-action@v5
  id: meta
  with:
    images: ghcr.io/${{ github.repository }}
    tags: |
      type=semver,pattern={{version}}
      type=ref,event=branch
- uses: docker/build-push-action@v6
  with:
    tags: ${{ steps.meta.outputs.tags }}

Wrong: Pushing images on pull requests

# ❌ BAD — PR builds should validate, not push
- uses: docker/build-push-action@v6
  with:
    push: true

Correct: Conditional push based on event type

# ✅ GOOD — only push on push events
- uses: docker/build-push-action@v6
  with:
    push: ${{ github.event_name != 'pull_request' }}

Common Pitfalls

Diagnostic Commands

# Check Docker version on runner
docker version

# Inspect Buildx builders
docker buildx ls

# Check image manifest (multi-platform)
docker manifest inspect ghcr.io/owner/repo:tag

# List GHCR packages via CLI
gh api user/packages?package_type=container

# View GHA cache usage
gh cache list

# Debug Buildx build locally
docker buildx build --progress=plain --no-cache .

Version History & Compatibility

VersionStatusBreaking ChangesMigration Notes
docker/build-push-action@v6CurrentRequires Buildx v0.15+Update setup-buildx to @v3
docker/build-push-action@v5DeprecatedReplace @v5 with @v6
docker/metadata-action@v5CurrentNew tag type syntaxReview tag configuration
docker/login-action@v3Current
docker/setup-buildx-action@v3Current
GHCR (ghcr.io)StableReplaced docker.pkg.github.com

When to Use / When Not to Use

Use WhenDon't Use WhenUse Instead
Building container images in GitHub CIBuilding in GitLab CIGitLab CI kaniko or DinD
Pushing to GHCR, Docker Hub, or ECRNeed image scanning, signing, SBOMTekton or Harbor
Need multi-platform imagesNeed GPU-accelerated buildsSelf-hosted runners
Simple container workflowsNeed 50+ image variantsCustom build matrix

Important Caveats

Related Units