Publish AnythingLLM Docker image on Release (amd64 & arm64) workflow (Mintplex-Labs/anything-llm)
The Publish AnythingLLM Docker image on Release (amd64 & arm64) workflow from Mintplex-Labs/anything-llm, explained and optimized by Latchkey.
CI health: B - good
Point runs-on at Latchkey and get job timeouts, SHA-pinned actions, self-healing for flaky steps, and up to 58% lower cost, applied automatically.
What it does
This is the Publish AnythingLLM Docker image on Release (amd64 & arm64) workflow from the Mintplex-Labs/anything-llm repository, a real project running GitHub Actions. It is shown here with attribution under its MIT license.
Below, Latchkey shows a faster, safer version produced by its optimization engine.
The workflow
name: Publish AnythingLLM Docker image on Release (amd64 & arm64)
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
release:
types: [published]
jobs:
push_multi_platform_to_registries:
name: Push Docker multi-platform image to multiple registries
runs-on: ubuntu-22.04-arm
permissions:
packages: write
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Check if DockerHub build needed
shell: bash
run: |
# Check if the secret for USERNAME is set (don't even check for the password)
if [[ -z "${{ secrets.DOCKER_USERNAME }}" ]]; then
echo "DockerHub build not needed"
echo "enabled=false" >> $GITHUB_OUTPUT
else
echo "DockerHub build needed"
echo "enabled=true" >> $GITHUB_OUTPUT
fi
id: dockerhub
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: v0.22.0
- name: Log in to Docker Hub
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
# Only login to the Docker Hub if the repo is mintplex/anythingllm, to allow for forks to build on GHCR
if: steps.dockerhub.outputs.enabled == 'true'
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: |
${{ steps.dockerhub.outputs.enabled == 'true' && 'mintplexlabs/anythingllm' || '' }}
ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Build and push multi-platform Docker image
uses: docker/build-push-action@v6
with:
context: .
file: ./docker/Dockerfile
push: true
sbom: true
provenance: mode=max
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# For Docker scout there are some intermediary reported CVEs which exists outside
# of execution content or are unreachable by an attacker but exist in image.
# We create VEX files for these so they don't show in scout summary.
- name: Collect known and verified CVE exceptions
id: cve-list
run: |
# Collect CVEs from filenames in vex folder
CVE_NAMES=""
for file in ./docker/vex/*.vex.json; do
[ -e "$file" ] || continue
filename=$(basename "$file")
stripped_filename=${filename%.vex.json}
CVE_NAMES+=" $stripped_filename"
done
echo "CVE_EXCEPTIONS=$CVE_NAMES" >> $GITHUB_OUTPUT
shell: bash
# About VEX attestations https://docs.docker.com/scout/explore/exceptions/
# Justifications https://github.com/openvex/spec/blob/main/OPENVEX-SPEC.md#status-justifications
- name: Add VEX attestations
env:
CVE_EXCEPTIONS: ${{ steps.cve-list.outputs.CVE_EXCEPTIONS }}
run: |
echo $CVE_EXCEPTIONS
curl -sSfL https://raw.githubusercontent.com/docker/scout-cli/main/install.sh | sh -s --
for cve in $CVE_EXCEPTIONS; do
for tag in "${{ join(fromJSON(steps.meta.outputs.json).tags, ' ') }}"; do
echo "Attaching VEX exception $cve to $tag"
docker scout attestation add \
--file "./docker/vex/$cve.vex.json" \
--predicate-type https://openvex.dev/ns/v0.2.0 \
$tag
done
done
shell: bash
The same workflow, on Latchkey
Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.
name: Publish AnythingLLM Docker image on Release (amd64 & arm64) concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true on: release: types: [published] jobs: push_multi_platform_to_registries: timeout-minutes: 30 name: Push Docker multi-platform image to multiple registries runs-on: latchkey-small-arm permissions: packages: write contents: read steps: - name: Check out the repo uses: actions/checkout@v4 - name: Check if DockerHub build needed shell: bash run: | # Check if the secret for USERNAME is set (don't even check for the password) if [[ -z "${{ secrets.DOCKER_USERNAME }}" ]]; then echo "DockerHub build not needed" echo "enabled=false" >> $GITHUB_OUTPUT else echo "DockerHub build needed" echo "enabled=true" >> $GITHUB_OUTPUT fi id: dockerhub - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 with: version: v0.22.0 - name: Log in to Docker Hub uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # Only login to the Docker Hub if the repo is mintplex/anythingllm, to allow for forks to build on GHCR if: steps.dockerhub.outputs.enabled == 'true' with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Log in to the Container registry uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 with: images: | ${{ steps.dockerhub.outputs.enabled == 'true' && 'mintplexlabs/anythingllm' || '' }} ghcr.io/${{ github.repository }} tags: | type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} - name: Build and push multi-platform Docker image uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile push: true sbom: true provenance: mode=max platforms: linux/amd64,linux/arm64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max # For Docker scout there are some intermediary reported CVEs which exists outside # of execution content or are unreachable by an attacker but exist in image. # We create VEX files for these so they don't show in scout summary. - name: Collect known and verified CVE exceptions id: cve-list run: | # Collect CVEs from filenames in vex folder CVE_NAMES="" for file in ./docker/vex/*.vex.json; do [ -e "$file" ] || continue filename=$(basename "$file") stripped_filename=${filename%.vex.json} CVE_NAMES+=" $stripped_filename" done echo "CVE_EXCEPTIONS=$CVE_NAMES" >> $GITHUB_OUTPUT shell: bash # About VEX attestations https://docs.docker.com/scout/explore/exceptions/ # Justifications https://github.com/openvex/spec/blob/main/OPENVEX-SPEC.md#status-justifications - name: Add VEX attestations env: CVE_EXCEPTIONS: ${{ steps.cve-list.outputs.CVE_EXCEPTIONS }} run: | echo $CVE_EXCEPTIONS curl -sSfL https://raw.githubusercontent.com/docker/scout-cli/main/install.sh | sh -s -- for cve in $CVE_EXCEPTIONS; do for tag in "${{ join(fromJSON(steps.meta.outputs.json).tags, ' ') }}"; do echo "Attaching VEX exception $cve to $tag" docker scout attestation add \ --file "./docker/vex/$cve.vex.json" \ --predicate-type https://openvex.dev/ns/v0.2.0 \ $tag done done shell: bash
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. - Add a job timeout so a hung step cannot burn hours of runner time.
2 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
- Network fetches
This workflow runs 1 job per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.