23 KiB
title | description | keywords |
---|---|---|
Example workflows | Docker GitHub Actions workflow examples. | ci, github actions, gha, examples |
This page showcases different examples of how you can customize and use the Docker GitHub Actions in your CI pipelines.
Push to multi-registries
The following workflow will connect you to Docker Hub and GitHub Container Registry{:target="blank" rel="noopener" class=""} and push the image to both registries:
{% raw %}
name: ci
on:
push:
branches:
- "main"
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
user/app:latest
user/app:1.0.0
ghcr.io/user/app:latest
ghcr.io/user/app:1.0.0
{% endraw %}
Manage tags and labels
If you want an "automatic" tag management and OCI Image Format Specification{:target="blank" rel="noopener" class=""} for labels, you can do it in a dedicated setup step. The following workflow will use the Docker Metadata Action{:target="blank" rel="noopener" class=""} to handle tags and labels based on GitHub Actions events and Git metadata:
{% raw %}
name: ci
on:
schedule:
- cron: "0 10 * * *"
push:
branches:
- "**"
tags:
- "v*.*.*"
pull_request:
branches:
- "main"
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
# list of Docker images to use as base name for tags
images: |
name/app
ghcr.io/username/app
# generate Docker tags based on the following events/attributes
tags: |
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Login to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Login to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
{% endraw %}
Multi-platform images
You can build multi-platform images using
the platforms
option, as described in the following example.
Note
- For a list of available platforms, see the Docker Setup Buildx{:target="blank" rel="noopener" class=""} action.
- If you want support for more platforms, you can use QEMU with the Docker Setup QEMU{:target="blank" rel="noopener" class=""} action.
{% raw %}
name: ci
on:
push:
branches:
- "main"
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: user/app:latest
{% endraw %}
Cache
This page contains examples on using the cache storage backends with GitHub actions.
Note
See Cache storage backends for more details about cache storage backends.
Inline cache
In most cases you want to use the inline cache exporter.
However, note that the inline
cache exporter only supports min
cache mode.
To use max
cache mode, push the image and the cache separately using the
registry cache exporter with the cache-to
option, as shown in the registry cache example.
{% raw %}
name: ci
on:
push:
branches:
- "main"
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: user/app:latest
cache-from: type=registry,ref=user/app:latest
cache-to: type=inline
{% endraw %}
Registry cache
You can import/export cache from a cache manifest or (special) image configuration on the registry with the registry cache exporter.
{% raw %}
name: ci
on:
push:
branches:
- "main"
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: user/app:latest
cache-from: type=registry,ref=user/app:buildcache
cache-to: type=registry,ref=user/app:buildcache,mode=max
{% endraw %}
GitHub cache
Cache backend API
Warning
This cache exporter is experimental. Please provide feedback on BuildKit repository{:target="blank" rel="noopener" class=""} if you experience any issues. {: .warning }
The GitHub Actions cache exporter
backend uses the GitHub Cache API
to fetch and upload cache blobs. That's why you should only use this cache
backend in a GitHub Action workflow, as the url
($ACTIONS_CACHE_URL
) and
token
($ACTIONS_RUNTIME_TOKEN
) attributes only get populated in a workflow
context.
{% raw %}
name: ci
on:
push:
branches:
- "main"
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: user/app:latest
cache-from: type=gha
cache-to: type=gha,mode=max
{% endraw %}
Local cache
Warning
At the moment, old cache entries aren't deleted, so the cache size keeps growing{:target="blank" rel="noopener" class=""}. The following example uses the
Move cache
step as a workaround (seemoby/buildkit#1896
{:target="blank" rel="noopener" class=""} for more info). {: .warning }
You can also leverage GitHub cache{:target="blank" rel="noopener" class=""} using the actions/cache and local cache exporter with this action:
{% raw %}
name: ci
on:
push:
branches:
- "main"
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Cache Docker layers
uses: actions/cache@v3
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
-
name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: user/app:latest
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
-
# Temp fix
# https://github.com/docker/build-push-action/issues/252
# https://github.com/moby/buildkit/issues/1896
name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
{% endraw %}
Secrets
In the following example uses and exposes the GITHUB_TOKEN
secret{:target="blank" rel="noopener" class=""}
as provided by GitHub in your workflow.
First, create a Dockerfile
that uses the secret:
# syntax=docker/dockerfile:1
FROM alpine
RUN --mount=type=secret,id=github_token \
cat /run/secrets/github_token
In this example, the secret name is github_token
. The following workflow
exposes this secret using the secrets
input:
{% raw %}
name: ci
on:
push:
branches:
- "main"
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Build
uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64,linux/arm64
tags: user/app:latest
secrets: |
"github_token=${{ secrets.GITHUB_TOKEN }}"
{% endraw %}
Note
You can also expose a secret file to the build with the
secret-files
input:secret-files: | "MY_SECRET=./secret.txt"
If you're using GitHub secrets{:target="blank" rel="noopener" class=""} and need to handle multi-line value, you will need to place the key-value pair between quotes:
{% raw %}
secrets: |
"MYSECRET=${{ secrets.GPG_KEY }}"
GIT_AUTH_TOKEN=abcdefghi,jklmno=0123456789
"MYSECRET=aaaaaaaa
bbbbbbb
ccccccccc"
FOO=bar
"EMPTYLINE=aaaa
bbbb
ccc"
"JSON_SECRET={""key1"":""value1"",""key2"":""value2""}"
{% endraw %}
Key | Value |
---|---|
MYSECRET |
*********************** |
GIT_AUTH_TOKEN |
abcdefghi,jklmno=0123456789 |
MYSECRET |
aaaaaaaa\nbbbbbbb\nccccccccc |
FOO |
bar |
EMPTYLINE |
aaaa\n\nbbbb\nccc |
JSON_SECRET |
{"key1":"value1","key2":"value2"} |
Note
Double escapes are needed for quote signs.
Export image to Docker
You may want your build result to be available in the Docker client through
docker images
to be able to use it in another step of your workflow:
name: ci
on:
push:
branches:
- "main"
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Build
uses: docker/build-push-action@v4
with:
context: .
load: true
tags: myimage:latest
-
name: Inspect
run: |
docker image inspect myimage:latest
Test your image before pushing it
In some cases, you might want to validate that the image works as expected before pushing it.
The following workflow implements several steps to achieve this:
- Build and export the image to Docker
- Test your image
- Multi-platform build and push the image
{% raw %}
name: ci
on:
push:
branches:
- "main"
env:
TEST_TAG: user/app:test
LATEST_TAG: user/app:latest
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and export to Docker
uses: docker/build-push-action@v4
with:
context: .
load: true
tags: ${{ env.TEST_TAG }}
-
name: Test
run: |
docker run --rm ${{ env.TEST_TAG }}
-
name: Build and push
uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ env.LATEST_TAG }}
{% endraw %}
Note
This workflow doesn't actually build the
linux/amd64
image twice. The image is built once, and the following steps uses the internal cache for from the firstBuild and push
step. The secondBuild and push
step only buildslinux/arm64
.
Local registry
For testing purposes you may need to create a local registry{:target="blank" rel="noopener" class=""} to push images into:
name: ci
on:
push:
branches:
- "main"
jobs:
docker:
runs-on: ubuntu-latest
services:
registry:
image: registry:2
ports:
- 5000:5000
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
driver-opts: network=host
-
name: Build and push to local registry
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: localhost:5000/name/app:latest
-
name: Inspect
run: |
docker buildx imagetools inspect localhost:5000/name/app:latest
Share built image between jobs
As each job is isolated in its own runner, you can't use your built image between jobs, except if you're using self-hosted runners{:target="blank" rel="noopener" class=""} However, you can pass data between jobs{:target="blank" rel="noopener" class=""} in a workflow using the actions/upload-artifact{:target="blank" rel="noopener" class=""} and actions/download-artifact{:target="blank" rel="noopener" class=""} actions:
name: ci
on:
push:
branches:
- "main"
jobs:
build:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Build and export
uses: docker/build-push-action@v4
with:
context: .
tags: myimage:latest
outputs: type=docker,dest=/tmp/myimage.tar
-
name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: myimage
path: /tmp/myimage.tar
use:
runs-on: ubuntu-latest
needs: build
steps:
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Download artifact
uses: actions/download-artifact@v3
with:
name: myimage
path: /tmp
-
name: Load image
run: |
docker load --input /tmp/myimage.tar
docker image ls -a
Named contexts
You can define additional build contexts,
and access them in your Dockerfile with FROM name
or --from=name
. When
Dockerfile defines a stage with the same name it's overwritten.
This can be useful with GitHub Actions to reuse results from other builds or pin an image to a specific tag in your workflow.
Pin image to a tag
Replace alpine:latest
with a pinned one:
# syntax=docker/dockerfile:1
FROM alpine
RUN echo "Hello World"
name: ci
on:
push:
branches:
- "main"
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Build
uses: docker/build-push-action@v4
with:
context: .
build-contexts: |
alpine=docker-image://alpine:3.16
tags: myimage:latest
Use image in subsequent steps
By default, the Docker Setup Buildx{:target="blank" rel="noopener" class=""}
action uses docker-container
as a build driver, so built Docker images aren't
loaded automatically.
With named contexts you can reuse the built image:
# syntax=docker/dockerfile:1
FROM alpine
RUN echo "Hello World"
name: ci
on:
push:
branches:
- "main"
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Build base image
uses: docker/build-push-action@v4
with:
context: base
load: true
tags: my-base-image:latest
-
name: Build
uses: docker/build-push-action@v4
with:
context: .
build-contexts: |
alpine=docker-image://my-base-image:latest
tags: myimage:latest
Copy images between registries
Multi-platform images built using Buildx can
be copied from one registry to another using the buildx imagetools create
command:
{% raw %}
name: ci
on:
push:
branches:
- "main"
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
user/app:latest
user/app:1.0.0
-
name: Push image to GHCR
run: |
docker buildx imagetools create \
--tag ghcr.io/user/app:latest \
--tag ghcr.io/user/app:1.0.0 \
user/app:latest
{% endraw %}
Update Docker Hub repository description
You can update the Docker Hub repository description using a third party action called Docker Hub Description{:target="blank" rel="noopener" class=""} with this action:
{% raw %}
name: ci
on:
push:
branches:
- "main"
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: user/app:latest
-
name: Update repo description
uses: peter-evans/dockerhub-description@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
repository: user/app
{% endraw %}