docker/build-push-action@v6, docker/metadata-action@v5, docker/setup-buildx-action@v3) to build, tag, and push container images to any registry with layer caching and multi-platform support.docker/build-push-action@v6 with push: true and tags from docker/metadata-action@v5.GITHUB_TOKEN.docker/setup-buildx-action@v3 before docker/build-push-action -- Buildx is required for layer caching and multi-platform builds.packages: write permission explicitly when pushing to GHCR.@v6) -- never use @main.| Action | Version | Purpose | Key Inputs |
|---|---|---|---|
docker/setup-qemu-action | @v3 | Enable multi-platform via QEMU | platforms |
docker/setup-buildx-action | @v3 | Create Buildx builder | driver-opts |
docker/login-action | @v3 | Authenticate to registry | registry, username, password |
docker/metadata-action | @v5 | Generate tags/labels from Git | images, tags |
docker/build-push-action | @v6 | Build and push image | context, push, tags, platforms, cache |
| GHCR login | — | GitHub Container Registry | registry: ghcr.io, GITHUB_TOKEN |
| Docker Hub login | — | Docker Hub | DOCKERHUB_TOKEN secret |
| ECR login | @v2 | AWS ECR | AWS credentials |
| Cache (GHA) | — | GitHub Actions cache backend | type=gha, mode=max |
| Cache (registry) | — | Registry as cache | type=registry, ref=... |
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
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.
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.
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.
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.
# .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
# .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
# ❌ BAD — docker build without Buildx cannot use layer caching
- run: docker build -t myimage:latest .
- run: docker push myimage:latest
# ✅ 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
# ❌ BAD — hardcoded tags don't track versions
- uses: docker/build-push-action@v6
with:
tags: ghcr.io/myorg/myapp:latest
# ✅ 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 }}
# ❌ BAD — PR builds should validate, not push
- uses: docker/build-push-action@v6
with:
push: true
# ✅ GOOD — only push on push events
- uses: docker/build-push-action@v6
with:
push: ${{ github.event_name != 'pull_request' }}
permissions: packages: write. [src3]mode=max sparingly or switch to registry cache. [src1].dockerignore. [src2]# 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 | Status | Breaking Changes | Migration Notes |
|---|---|---|---|
| docker/build-push-action@v6 | Current | Requires Buildx v0.15+ | Update setup-buildx to @v3 |
| docker/build-push-action@v5 | Deprecated | — | Replace @v5 with @v6 |
| docker/metadata-action@v5 | Current | New tag type syntax | Review tag configuration |
| docker/login-action@v3 | Current | — | — |
| docker/setup-buildx-action@v3 | Current | — | — |
| GHCR (ghcr.io) | Stable | — | Replaced docker.pkg.github.com |
| Use When | Don't Use When | Use Instead |
|---|---|---|
| Building container images in GitHub CI | Building in GitLab CI | GitLab CI kaniko or DinD |
| Pushing to GHCR, Docker Hub, or ECR | Need image scanning, signing, SBOM | Tekton or Harbor |
| Need multi-platform images | Need GPU-accelerated builds | Self-hosted runners |
| Simple container workflows | Need 50+ image variants | Custom build matrix |
docker/build-push-action builds on the runner, not inside a container.latest tag should only be applied to production-ready releases.