Release Docker Image workflow (offen/docker-volume-backup)
The Release Docker Image workflow from offen/docker-volume-backup, explained and optimized by Latchkey.
CI health: C - fair
Point runs-on at Latchkey and get run de-duplication, job timeouts, SHA-pinned actions, self-healing for flaky steps, and up to 58% lower cost, applied automatically.
What it does
This is the Release Docker Image workflow from the offen/docker-volume-backup repository, a real project running GitHub Actions. It is shown here with attribution under its MPL-2.0 license.
Below, Latchkey shows a faster, safer version produced by its optimization engine.
The workflow
name: Release Docker Image
on:
push:
tags: v**
jobs:
push_to_registries:
name: Push Docker image to multiple registries
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: set Environment Variables
id: env
run: |
echo "NOW=$(date +'%F %Z %T')" >> $GITHUB_ENV
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
# list of Docker images to use as base name for tags
images: |
offen/docker-volume-backup
ghcr.io/offen/docker-volume-backup
# define global behaviour for tags
flavor: |
latest=false
# specify one tag which never gets set, to prevent the tag-attribute being empty, as it will fallback to a default
tags: |
# output v2.42.1-alpha.1 (incl. pre-releases)
type=semver,pattern=v{{version}},enable=false
labels: |
org.opencontainers.image.title=${{github.event.repository.name}}
org.opencontainers.image.description=Backup Docker volumes locally or to any S3, WebDAV, Azure Blob Storage, Dropbox or SSH compatible storage
org.opencontainers.image.vendor=${{github.repository_owner}}
org.opencontainers.image.licenses=MPL-2.0
org.opencontainers.image.version=${{github.ref_name}}
org.opencontainers.image.created=${{ env.NOW }}
org.opencontainers.image.source=${{github.server_url}}/${{github.repository}}
org.opencontainers.image.revision=${{github.sha}}
org.opencontainers.image.url=https://offen.github.io/docker-volume-backup/
org.opencontainers.image.documentation=https://offen.github.io/docker-volume-backup/
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker tags
id: tags
run: |
version_tag="${{github.ref_name}}"
tags=($version_tag)
if [[ "$version_tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
# prerelease tags like `v2.0.0-alpha.1` should not be released as `latest` nor `v2`
tags+=("latest")
tags+=($(echo "$version_tag" | cut -d. -f1))
fi
releases=""
for tag in "${tags[@]}"; do
releases="${releases:+$releases,}offen/docker-volume-backup:$tag,ghcr.io/offen/docker-volume-backup:$tag"
done
echo "releases=$releases" >> "$GITHUB_OUTPUT"
- name: Build and push Docker images
uses: docker/build-push-action@v5
with:
context: .
push: true
platforms: linux/amd64,linux/arm64,linux/arm/v7
tags: ${{ steps.tags.outputs.releases }}
labels: ${{ steps.meta.outputs.labels }}
The same workflow, on Latchkey
Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.
name: Release Docker Image on: push: tags: v** concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: push_to_registries: timeout-minutes: 30 name: Push Docker image to multiple registries runs-on: latchkey-small permissions: packages: write contents: read steps: - name: Check out the repo uses: actions/checkout@v4 - name: set Environment Variables id: env run: | echo "NOW=$(date +'%F %Z %T')" >> $GITHUB_ENV - name: Docker meta id: meta uses: docker/metadata-action@v5 with: # list of Docker images to use as base name for tags images: | offen/docker-volume-backup ghcr.io/offen/docker-volume-backup # define global behaviour for tags flavor: | latest=false # specify one tag which never gets set, to prevent the tag-attribute being empty, as it will fallback to a default tags: | # output v2.42.1-alpha.1 (incl. pre-releases) type=semver,pattern=v{{version}},enable=false labels: | org.opencontainers.image.title=${{github.event.repository.name}} org.opencontainers.image.description=Backup Docker volumes locally or to any S3, WebDAV, Azure Blob Storage, Dropbox or SSH compatible storage org.opencontainers.image.vendor=${{github.repository_owner}} org.opencontainers.image.licenses=MPL-2.0 org.opencontainers.image.version=${{github.ref_name}} org.opencontainers.image.created=${{ env.NOW }} org.opencontainers.image.source=${{github.server_url}}/${{github.repository}} org.opencontainers.image.revision=${{github.sha}} org.opencontainers.image.url=https://offen.github.io/docker-volume-backup/ org.opencontainers.image.documentation=https://offen.github.io/docker-volume-backup/ - name: Set up QEMU uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Log in to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Log in to GHCR uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract Docker tags id: tags run: | version_tag="${{github.ref_name}}" tags=($version_tag) if [[ "$version_tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then # prerelease tags like `v2.0.0-alpha.1` should not be released as `latest` nor `v2` tags+=("latest") tags+=($(echo "$version_tag" | cut -d. -f1)) fi releases="" for tag in "${tags[@]}"; do releases="${releases:+$releases,}offen/docker-volume-backup:$tag,ghcr.io/offen/docker-volume-backup:$tag" done echo "releases=$releases" >> "$GITHUB_OUTPUT" - name: Build and push Docker images uses: docker/build-push-action@v5 with: context: . push: true platforms: linux/amd64,linux/arm64,linux/arm/v7 tags: ${{ steps.tags.outputs.releases }} labels: ${{ steps.meta.outputs.labels }}
What changed
- Run on Latchkey managed runners with one line (
runs-on), which apply the fixes below automatically and self-heal transient failures. This example useslatchkey-small; pick the runner size that fits the job. - Cancel superseded runs when a branch or PR gets a newer push.
- Add a job timeout so a hung step cannot burn hours of runner time.
5 third-party actions are referenced by a movable tag. Pin them to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.
What Latchkey heals here
This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:
- Container pulls and builds
This workflow runs 1 job per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.