Docker cookbook · Intermediate
Build, cache and push images in GitHub Actions
Build images in CI with layer caching and push them with immutable tags.
Approach
Use the official actions: set up Buildx, log in, compute tags with metadata-action (commit SHA and semver), and build-push with the GitHub Actions cache.
Solution
name: image
on: {push: {branches: [main], tags: ["v*"]}}
permissions: {contents: read, packages: write}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- 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 }}
tags: |
type=sha
type=semver,pattern={{version}}
- uses: docker/build-push-action@v6
with:
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
Pitfalls
- Pushing only
latest, so deployments can't pin a build. - Building without cache on every run.
- Long-lived registry passwords instead of the job token or OIDC.
Variations
- Add
platforms: linux/amd64,linux/arm64for multi-platform images. - Add provenance and SBOM attestations (
provenance: true,sbom: true).
Further reading: Docker build GitHub Actions
Reference: https://docs.docker.com/build/ci/github-actions/
More Docker recipes
- Add a healthcheck to an HTTP service
- Build a layered image for a Spring Boot app
- Build a production image for a Node.js service
- Build a production image for a Python web app
- Build one image tag for amd64 and arm64
- Configure nginx from environment variables
- Develop with Compose and live reload
- Make containers cope with dependencies that start slowly